package com.own.component.elastic.util;

import co.elastic.clients.elasticsearch._types.aggregations.CalendarInterval;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * TimeRangeResolveUtil
 *
 * @author chenxueli
 * @date 2024-05-08 04:29:00
 */
public class TimeRangeResolveUtil {

    private static final DateTimeFormatter FORMATTER_SECOND = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter FORMATTER_MINUTE = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    private static final DateTimeFormatter FORMATTER_HOUR = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
    private static final DateTimeFormatter FORMATTER_DAY = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter FORMATTER_MONTH = DateTimeFormatter.ofPattern("yyyy-MM");
    private static final DateTimeFormatter FORMATTER_QUARTER = DateTimeFormatter.ofPattern("yyyy-QQ");
    private static final DateTimeFormatter FORMATTER_YEAR = DateTimeFormatter.ofPattern("yyyy");

    /**
     * 根据时间统计范围解析时间范围
     *
     * @param interval  时间统计范围
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolve(CalendarInterval interval, LocalDateTime startTime, LocalDateTime endTime) {
        return switch (interval) {
            case Second -> resolveForSecond(startTime, endTime);
            case Minute -> resolveForMinute(startTime, endTime);
            case Hour -> resolveForHour(startTime, endTime);
            case Day -> resolveForDay(startTime, endTime);
            case Week -> resolveForWeek(startTime, endTime);
            case Month -> resolveForMonth(startTime, endTime);
            case Quarter -> resolveForQuarter(startTime, endTime);
            case Year -> resolveForYear(startTime, endTime);
            default -> throw new IllegalArgumentException("Unsupported interval: " + interval);
        };
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForSecond(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = startTime;
        do {
            resultList.add(tempStartTime.format(FORMATTER_SECOND));
            tempStartTime = tempStartTime.plusSeconds(1);
        } while (!endTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForMinute(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = LocalDateTime.of(startTime.getYear(), startTime.getMonth(), startTime.getDayOfMonth(), startTime.getHour(), startTime.getMinute(), 0);
        var tempEndTime = LocalDateTime.of(endTime.getYear(), endTime.getMonth(), endTime.getDayOfMonth(), endTime.getHour(), endTime.getMinute(), 0);
        do {
            resultList.add(tempStartTime.format(FORMATTER_MINUTE));
            tempStartTime = tempStartTime.plusMinutes(1);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForHour(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = LocalDateTime.of(startTime.getYear(), startTime.getMonth(), startTime.getDayOfMonth(), startTime.getHour(), 0, 0);
        var tempEndTime = LocalDateTime.of(endTime.getYear(), endTime.getMonth(), endTime.getDayOfMonth(), endTime.getHour(), 0, 0);
        do {
            resultList.add(tempStartTime.format(FORMATTER_HOUR));
            tempStartTime = tempStartTime.plusHours(1);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForDay(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = startTime.toLocalDate();
        var tempEndTime = endTime.toLocalDate();
        do {
            resultList.add(tempStartTime.format(FORMATTER_DAY));
            tempStartTime = tempStartTime.plusDays(1);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForWeek(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = startTime.toLocalDate();
        var tempEndTime = endTime.toLocalDate();
        do {
            resultList.add(tempStartTime.format(FORMATTER_DAY));
            tempStartTime = tempStartTime.plusWeeks(1);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForMonth(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = LocalDate.of(startTime.getYear(), startTime.getMonth(), 1);
        var tempEndTime = LocalDate.of(endTime.getYear(), endTime.getMonth(), 1);
        do {
            resultList.add(tempStartTime.format(FORMATTER_MONTH));
            tempStartTime = tempStartTime.plusMonths(1);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForQuarter(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = LocalDate.of(startTime.getYear(), startTime.getMonth(), 1);
        var tempEndTime = LocalDate.of(endTime.getYear(), endTime.getMonth(), 1);
        do {
            resultList.add(tempStartTime.format(FORMATTER_QUARTER));
            tempStartTime = tempStartTime.plusMonths(3);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    /**
     * 根据开始时间和结束时间获取时间范围
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 时间范围
     */
    public static List<String> resolveForYear(LocalDateTime startTime, LocalDateTime endTime) {
        if (endTime.isBefore(startTime)) {
            throw new IllegalArgumentException("endTime must be after startTime");
        }
        var resultList = new ArrayList<String>();
        var tempStartTime = LocalDate.of(startTime.getYear(), 1, 1);
        var tempEndTime = LocalDate.of(endTime.getYear(), 1, 1);
        do {
            resultList.add(tempStartTime.format(FORMATTER_YEAR));
            tempStartTime = tempStartTime.plusYears(1);
        } while (!tempEndTime.isBefore(tempStartTime));
        return resultList;
    }

    public static void main(String[] args) {
        var startTime = LocalDateTime.of(2024, 5, 8, 4, 29, 0);
        var endTime = LocalDateTime.of(2024, 6, 8, 10, 30, 0);
        System.out.println("day");
        resolve(CalendarInterval.Day, startTime, endTime).forEach(System.out::println);
        System.out.println("month");
        resolve(CalendarInterval.Month, startTime, endTime).forEach(System.out::println);
        System.out.println("year");
        resolve(CalendarInterval.Year, startTime, endTime).forEach(System.out::println);
    }

}
